<?php

declare(strict_types=1);

namespace app\service\user;

use app\model\Org;
use app\model\OrgAddress;
use app\model\OrgContact;
use app\model\OrgMember;
use app\model\OrgProfile;
use app\model\Region;
use app\model\User;
use app\model\UserCompany;
use app\model\UserIdentity;
use app\service\notify\CreateNotifyService;
use app\service\org\OrgAuditService;
use app\service\sys\ConfigService;
use InvalidArgumentException;
use TencentCloud\Common\Credential;
use TencentCloud\Common\Exception\TencentCloudSDKException;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
use TencentCloud\Ocr\V20181119\Models\BizLicenseOCRRequest;
use TencentCloud\Ocr\V20181119\OcrClient;
use think\facade\Db;

class CompanyVerificationService
{
    public function __construct(
        private ConfigService $configService,
        private OrgAuditService $auditService,
        private CreateNotifyService $notifyService
    ) {
    }

    public function getCompany(int $userId): ?UserCompany
    {
        return UserCompany::where('user_id', $userId)->find();
    }

    public function format(?UserCompany $company): ?array
    {
        if (!$company) {
            return null;
        }

        $extra = $this->normalizeExtra($company->extra ?? null);

        return [
            'company_name' => $company->company_name,
            'license_code' => $company->license_code,
            'license_photo' => $company->license_photo,
            'legal_rep' => $company->legal_rep,
            'address' => $company->address,
            'scope' => $company->scope,
            'region_id' => $company->region_id ? (int) $company->region_id : ($extra['region_id'] ?? null),
            'invoice' => $extra['invoice'] ?? null,
            'addresses' => $extra['addresses'] ?? [],
            'raw' => $company->ocr_raw ?? $extra['raw'] ?? null,
            'status' => (int) $company->status,
            'verified_time' => $company->verified_at,
        ];
    }

    public function saveCompany(int $userId, array $data): UserCompany
    {
        $companyName = trim((string) ($data['company_name'] ?? ''));
        $licenseCode = trim((string) ($data['license_code'] ?? ''));
        $legalRep = trim((string) ($data['legal_rep'] ?? ''));
        $address = trim((string) ($data['address'] ?? ''));
        $scope = $data['scope'] ?? null;
        $licensePhoto = trim((string) ($data['license_photo'] ?? ''));
        $regionId = array_key_exists('region_id', $data) ? (int) $data['region_id'] : null;
        $raw = $data['raw'] ?? null;

        if ($companyName === '') {
            throw new InvalidArgumentException('企业名称不能为空');
        }
        if ($licenseCode === '') {
            throw new InvalidArgumentException('统一社会信用代码不能为空');
        }

        if ($regionId !== null && $regionId > 0) {
            $regionExists = Region::where('id', $regionId)->value('id');
            if (!$regionExists) {
                throw new InvalidArgumentException('所选区域不存在');
            }
        }

        $duplicate = UserCompany::where('license_code', $licenseCode)
            ->where('user_id', '<>', $userId)
            ->value('user_id');
        if ($duplicate) {
            throw new InvalidArgumentException('该企业已提交认证，请联系管理员或使用原账号登录');
        }

        return Db::transaction(function () use ($userId, $companyName, $licenseCode, $legalRep, $address, $scope, $licensePhoto, $regionId, $raw) {
            /** @var UserCompany|null $company */
            $company = UserCompany::where('user_id', $userId)
                ->lock(true)
                ->find();

            $wasPending = $company ? (int) $company->status === 0 : false;

            $payload = [
                'company_name' => $companyName,
                'license_code' => $licenseCode,
                'license_photo' => $licensePhoto,
                'legal_rep' => $legalRep,
                'address' => $address,
                'scope' => $scope,
                'status' => 0,
                'verified_at' => null,
            ];
            if ($raw !== null) {
                $payload['ocr_raw'] = is_array($raw)
                    ? $raw
                    : (is_string($raw) ? json_decode($raw, true) : $company?->ocr_raw);
            } elseif ($company && $company->ocr_raw !== null) {
                $payload['ocr_raw'] = $company->ocr_raw;
            }

            if ($regionId !== null && $regionId > 0) {
                $payload['region_id'] = $regionId;
            } elseif ($regionId === null && $company && $company->region_id !== null) {
                // keep existing region_id if present and not overridden
                $payload['region_id'] = $company->region_id;
            }

            if ($company) {
                $extra = $this->normalizeExtra($company->extra ?? null);
            } else {
                $extra = [];
            }

            if ($regionId !== null && $regionId > 0) {
                $extra['region_id'] = $regionId;
            }

            if ($extra !== []) {
                $payload['extra'] = $extra;
            }

            if ($company) {
                $company->save($payload);
            } else {
                $payload['user_id'] = $userId;
                $company = UserCompany::create($payload);
            }

            $this->ensureSellerIdentity($userId);

            $company = $company->refresh();

            if (!$wasPending && (int) $company->status === 0) {
                $this->notifyPendingAudit();
            }

            return $company;
        });
    }

    private function notifyPendingAudit(): void
    {
        try {
            $count = $this->auditService->countPending();
            if ($count <= 0) {
                return;
            }

            $adminRoleId = Db::name('auth_role')
                ->where('slug', 'admin')
                ->value('id');

            if (!$adminRoleId) {
                return;
            }

            $this->notifyService->add([
                [
                    'target_id' => 0,
                    'target_type' => 'admin',
                    'channel' => 'websocket',
                ],
            ], 'org_audit_pending', [
                'count' => $count,
                'route' => '/system/user/org-list?view=audit',
            ]);
        } catch (\Throwable) {
            // ignore notify failure
        }
    }

    public function saveInvoice(int $userId, array $payload): UserCompany
    {
        $company = $this->getCompanyOrFail($userId);

        $invoiceTitle = trim((string) ($payload['invoice_title'] ?? ''));
        $invoiceTaxNo = trim((string) ($payload['invoice_taxno'] ?? ''));
        if ($invoiceTitle === '' || $invoiceTaxNo === '') {
            throw new InvalidArgumentException('请填写开票抬头和纳税人识别号');
        }

        $invoice = [
            'invoice_title' => $invoiceTitle,
            'invoice_taxno' => $invoiceTaxNo,
            'bank_name' => trim((string) ($payload['bank_name'] ?? '')),
            'bank_account' => trim((string) ($payload['bank_account'] ?? '')),
            'bank_account_name' => trim((string) ($payload['bank_account_name'] ?? '')),
            'contact_name' => trim((string) ($payload['contact_name'] ?? '')),
            'contact_phone' => trim((string) ($payload['contact_phone'] ?? '')),
            'contact_email' => trim((string) ($payload['contact_email'] ?? '')),
        ];

        $extra = $this->normalizeExtra($company->extra ?? null);
        $extra['invoice'] = $invoice;

        $company->save(['extra' => $extra]);

        return $company->refresh();
    }

    public function saveAddresses(int $userId, array $payload): UserCompany
    {
        $company = $this->getCompanyOrFail($userId);
        $addresses = $payload['addresses'] ?? [];
        if (!is_array($addresses) || $addresses === []) {
            throw new InvalidArgumentException('请提供至少一个地址');
        }

        $normalized = [];
        foreach ($addresses as $address) {
            if (!is_array($address)) {
                continue;
            }
            $type = (string) ($address['address_type'] ?? 'shipping');
            if (!in_array($type, ['shipping', 'billing'], true)) {
                $type = 'shipping';
            }

            $normalized[] = [
                'address_type' => $type,
                'contact_name' => trim((string) ($address['contact_name'] ?? '')),
                'contact_phone' => trim((string) ($address['contact_phone'] ?? '')),
                'province' => trim((string) ($address['province'] ?? '')),
                'city' => trim((string) ($address['city'] ?? '')),
                'district' => trim((string) ($address['district'] ?? '')),
                'address' => trim((string) ($address['address'] ?? '')),
                'postal_code' => trim((string) ($address['postal_code'] ?? '')),
                'is_default' => isset($address['is_default']) ? (int) $address['is_default'] : ($type === 'shipping' ? 1 : 0),
            ];
        }

        if ($normalized === []) {
            throw new InvalidArgumentException('请提供有效的地址信息');
        }

        $extra = $this->normalizeExtra($company->extra ?? null);
        $extra['addresses'] = $normalized;

        $company->save(['extra' => $extra]);

        return $company->refresh();
    }

    public function approveCompany(int $userId, array $options = []): array
    {
        $skipNotify = !empty($options['skip_notify']);
        unset($options['skip_notify']);

        $result = Db::transaction(function () use ($userId, $options) {
            /** @var UserCompany|null $company */
            $company = UserCompany::where('user_id', $userId)
                ->lock(true)
                ->find();

            if (!$company) {
                throw new InvalidArgumentException('尚未提交企业认证资料');
            }

            $now = date('Y-m-d H:i:s');

            $company->save([
                'status' => 1,
                'verified_at' => $now,
            ]);

            $user = User::where('id', $userId)->lock(true)->find();
            if (!$user) {
                throw new InvalidArgumentException('用户不存在');
            }

            $extra = $this->normalizeExtra($company->extra ?? null);

            $resolvedRegionId = (int) ($options['region_id'] ?? 0);
            if ($resolvedRegionId <= 0 && $company->region_id) {
                $resolvedRegionId = (int) $company->region_id;
            }
            if ($resolvedRegionId <= 0 && isset($extra['region_id'])) {
                $resolvedRegionId = (int) $extra['region_id'];
            }

            $org = $this->ensureSellerOrg(
                $user,
                $company,
                $resolvedRegionId,
                (int) ($options['approved_by'] ?? 0),
                $now
            );

            if ($org->org_type !== Org::TYPE_BOTH) {
                $org->save(['org_type' => Org::TYPE_BOTH]);
                $org = $org->refresh();
            }

            $this->syncSellerIdentity($user, $org->id, $resolvedRegionId, $now);
            $this->syncBuyerIdentity($user, $org, $resolvedRegionId, $now);

            $this->syncOrgProfile($org->id, $company);
            $this->syncOrgContact($org->id, $extra['invoice'] ?? [], $company);
            $this->syncOrgAddresses($org->id, $extra['addresses'] ?? []);

            UserRoleManager::ensureRoles($user->id, [
                [
                    'slug' => 'seller_admin',
                    'org_id' => $org->id,
                    'region_id' => $resolvedRegionId > 0 ? $resolvedRegionId : $org->region_id,
                ],
                [
                    'slug' => 'buyer_admin',
                    'region_id' => $resolvedRegionId > 0 ? $resolvedRegionId : ($org->region_id ?? null),
                ],
            ]);

            return [
                'company' => $company->refresh(),
                'org' => $org->refresh(),
            ];
        });

        if (!$skipNotify) {
            $this->notifyPendingAudit();
        }

        return $result;
    }

    public function rejectCompany(int $userId, string $reason = ''): UserCompany
    {
        return Db::transaction(function () use ($userId, $reason) {
            /** @var UserCompany|null $company */
            $company = UserCompany::where('user_id', $userId)
                ->lock(true)
                ->find();

            if (!$company) {
                throw new InvalidArgumentException('尚未提交企业认证资料');
            }

            $company->save([
                'status' => 2,
                'verified_at' => null,
            ]);

            UserIdentity::where('user_id', $userId)
                ->where('identity_type', 'seller')
                ->update([
                    'status' => 0,
                    'verified_at' => null,
                    'remark' => $reason,
                ]);

            return $company->refresh();
        });
    }

    public function recognizeBusinessLicense(string $imageUrl): array
    {
        $imageUrl = trim($imageUrl);
        if ($imageUrl === '') {
            throw new InvalidArgumentException('营业执照图片地址不能为空');
        }

        $config = $this->configService->get('tencent.cloud_api', []);
        if (is_string($config)) {
            $config = json_decode($config, true) ?: [];
        }

        $secretId = (string) ($config['SecretId'] ?? $config['secretId'] ?? '');
        $secretKey = (string) ($config['SecretKey'] ?? $config['secretKey'] ?? '');
        $region = (string) ($config['Region'] ?? $config['region'] ?? 'ap-shanghai');

        if ($secretId === '' || $secretKey === '') {
            throw new InvalidArgumentException('腾讯云 OCR 未配置，请联系管理员');
        }

        try {
            $credential = new Credential($secretId, $secretKey);
            $httpProfile = new HttpProfile();
            $httpProfile->setEndpoint('ocr.tencentcloudapi.com');

            $clientProfile = new ClientProfile();
            $clientProfile->setHttpProfile($httpProfile);

            $client = new OcrClient($credential, $region, $clientProfile);

            $request = new BizLicenseOCRRequest();
            $request->fromJsonString(json_encode([
                'ImageUrl' => $imageUrl,
                'EnableImageEnhance' => true,
                'IncludeCutImage' => false,
                'EnablePdfRecognition' => false,
            ], JSON_UNESCAPED_UNICODE));

            $response = $client->BizLicenseOCR($request);

            $raw = json_decode($response->toJsonString(), true);

            return [
                'company_name' => (string) ($raw['CompanyName'] ?? $raw['Name'] ?? ''),
                'business_license_number' => (string) ($raw['UnifiedSocialCreditCode'] ?? $raw['RegNum'] ?? ''),
                'legal_representative' => (string) ($raw['LegalPerson'] ?? $raw['Person'] ?? ''),
                'company_address' => (string) ($raw['RegisteredAddress'] ?? $raw['Address'] ?? ''),
                'business_scope' => (string) ($raw['Business'] ?? ''),
                'established_date' => (string) ($raw['EstablishmentDate'] ?? $raw['SetDate'] ?? $raw['RegistrationDate'] ?? ''),
                'registered_capital' => (string) ($raw['RegisteredCapital'] ?? $raw['Capital'] ?? ''),
                'raw' => $raw,
            ];
        } catch (TencentCloudSDKException $exception) {
            throw new InvalidArgumentException('营业执照识别失败：' . $exception->getMessage());
        }
    }

    private function ensureSellerIdentity(int $userId): void
    {
        $identity = UserIdentity::where('user_id', $userId)
            ->where('identity_type', 'seller')
            ->find();

        if ($identity) {
            $identity->save([
                'status' => 0,
                'verified_at' => null,
            ]);
        } else {
            UserIdentity::create([
                'user_id' => $userId,
                'identity_type' => 'seller',
                'status' => 0,
            ]);
        }
    }

    private function ensureSellerOrg(
        User $user,
        UserCompany $company,
        int $regionId,
        int $approvedBy,
        string $now
    ): Org {
        $identity = UserIdentity::where('user_id', $user->id)
            ->where('identity_type', 'seller')
            ->lock(true)
            ->find();

        $invitedBy = null;
        if ($identity && $identity->invited_by) {
            $invitedBy = (int) $identity->invited_by;
        } elseif ($user->invited_by) {
            $invitedBy = (int) $user->invited_by;
        }
        $org = null;
        if ($identity && $identity->org_id) {
            $org = Org::where('id', $identity->org_id)->lock(true)->find();
        }

        if (!$org) {
            $org = Org::create([
                'name' => $company->company_name,
                'org_type' => Org::TYPE_SELLER,
                'logo_url' => '',
                'region_id' => $regionId > 0 ? $regionId : null,
                'status' => Org::STATUS_ACTIVE,
                'owner_user_id' => $user->id,
                'inviter_user_id' => $invitedBy ?: null,
                'approved_by' => $approvedBy ?: null,
                'approved_at' => $now,
            ]);

            OrgMember::create([
                'org_id' => $org->id,
                'user_id' => $user->id,
                'member_role' => 'owner',
                'status' => 1,
                'joined_at' => $now,
            ]);
        } else {
            $update = [
                'name' => $company->company_name,
                'region_id' => $regionId > 0 ? $regionId : $org->region_id,
            ];

            if ($org->owner_user_id === null) {
                $update['owner_user_id'] = $user->id;
            }

            if ($invitedBy && !$org->inviter_user_id) {
                $update['inviter_user_id'] = $invitedBy;
            }

            $org->save($update);
        }

        return $org->refresh();
    }

    private function syncSellerIdentity(User $user, int $orgId, int $regionId, string $now): void
    {
        $identity = UserIdentity::where('user_id', $user->id)
            ->where('identity_type', 'seller')
            ->lock(true)
            ->find();

        $payload = [
            'status' => 1,
            'org_id' => $orgId,
            'verified_at' => $now,
        ];

        if ($regionId > 0) {
            $payload['region_id'] = $regionId;
        }

        if ($identity) {
            $identity->save($payload);
        } else {
            UserIdentity::create(array_merge([
                'user_id' => $user->id,
                'identity_type' => 'seller',
            ], $payload));
        }
    }

    private function syncBuyerIdentity(User $user, Org $org, int $regionId, string $now): void
    {
        $identity = UserIdentity::where('user_id', $user->id)
            ->where('identity_type', 'buyer')
            ->lock(true)
            ->find();

        $payload = [
            'status' => 1,
            'org_id' => $org->id,
            'verified_at' => $now,
        ];

        if ($regionId > 0) {
            $payload['region_id'] = $regionId;
        }

        $invitedBy = $org->inviter_user_id ?: ($identity?->invited_by ?? $user->invited_by);
        if ($invitedBy) {
            $payload['invited_by'] = (int) $invitedBy;
        }

        if ($identity) {
            $identity->save($payload);
        } else {
            UserIdentity::create(array_merge([
                'user_id' => $user->id,
                'identity_type' => 'buyer',
            ], $payload));
        }
    }

    private function syncOrgProfile(int $orgId, UserCompany $company): void
    {
        $profile = OrgProfile::where('org_id', $orgId)->find();
        $extra = $this->normalizeExtra($company->extra ?? null);
        $raw = $company->ocr_raw ?? ($extra['raw'] ?? []);

        $data = [
            'license_number' => $company->license_code,
            'license_photo' => $company->license_photo,
            'tax_number' => $company->license_code,
            'legal_rep' => $company->legal_rep,
            'biz_scope' => $company->scope,
            'registered_capital' => $raw['RegisteredCapital'] ?? $raw['Capital'] ?? '',
            'established_at' => $this->resolveDate($raw['EstablishmentDate'] ?? $raw['SetDate'] ?? $raw['RegistrationDate'] ?? null),
            'extra' => $this->filterEmpty([
                'raw' => $raw,
            ]),
        ];

        if ($profile) {
            $profile->save($data);
        } else {
            OrgProfile::create(array_merge($data, ['org_id' => $orgId]));
        }
    }

    private function syncOrgContact(int $orgId, array $invoice, UserCompany $company): void
    {
        $contact = OrgContact::where('org_id', $orgId)->find();

        $data = [
            'contact_name' => (string) ($invoice['contact_name'] ?? ''),
            'contact_phone' => (string) ($invoice['contact_phone'] ?? ''),
            'contact_email' => (string) ($invoice['contact_email'] ?? ''),
            'bank_name' => (string) ($invoice['bank_name'] ?? ''),
            'bank_account' => (string) ($invoice['bank_account'] ?? ''),
            'bank_account_name' => (string) ($invoice['bank_account_name'] ?? ''),
            'invoice_title' => (string) ($invoice['invoice_title'] ?? $company->company_name),
            'invoice_taxno' => (string) ($invoice['invoice_taxno'] ?? $company->license_code),
            'extra' => $this->filterEmpty($invoice),
        ];

        if ($contact) {
            $contact->save($data);
        } else {
            OrgContact::create(array_merge($data, ['org_id' => $orgId]));
        }
    }

    private function syncOrgAddresses(int $orgId, array $addresses): void
    {
        if (!is_array($addresses) || $addresses === []) {
            return;
        }

        OrgAddress::where('org_id', $orgId)->delete();

        foreach ($addresses as $address) {
            if (!is_array($address)) {
                continue;
            }

            $addressType = in_array(($address['address_type'] ?? ''), ['shipping', 'billing'], true)
                ? $address['address_type']
                : 'shipping';

            OrgAddress::create([
                'org_id' => $orgId,
                'contact_name' => (string) ($address['contact_name'] ?? ''),
                'contact_phone' => (string) ($address['contact_phone'] ?? ''),
                'province' => (string) ($address['province'] ?? ''),
                'city' => (string) ($address['city'] ?? ''),
                'district' => (string) ($address['district'] ?? ''),
                'address' => (string) ($address['address'] ?? ''),
                'postal_code' => (string) ($address['postal_code'] ?? ''),
                'is_default' => isset($address['is_default'])
                    ? (int) $address['is_default']
                    : ($addressType === 'shipping' ? 1 : 0),
                'address_type' => $addressType,
            ]);
        }
    }

    private function normalizeExtra(mixed $extra): array
    {
        if (is_array($extra)) {
            return $extra;
        }
        if (is_string($extra)) {
            $decoded = json_decode($extra, true);
            return is_array($decoded) ? $decoded : [];
        }

        return [];
    }

    private function getCompanyOrFail(int $userId): UserCompany
    {
        $company = $this->getCompany($userId);
        if (!$company) {
            throw new InvalidArgumentException('请先完成企业主体信息填写');
        }

        return $company;
    }

    private function filterEmpty(array $data): array
    {
        return array_filter(
            $data,
            static fn($value) => $value !== null && $value !== '' && $value !== []
        );
    }

    private function resolveDate(?string $value): ?string
    {
        if (!$value) {
            return null;
        }

        $timestamp = strtotime($value);
        if ($timestamp === false) {
            return null;
        }

        return date('Y-m-d', $timestamp);
    }
}
